package Border;
import Square.RegularSquare;
import Square.Square;
import be.kuleuven.cs.som.annotate.*;

/**
 * Border class that is used for a square.
 *  
 * @author Koen Certyn & Michiel Meersmans
 * 	
 * @invar 		A square can only have one border in one direction
 */
public class Border {
	/**
	 * Constructs a new border
	 * 
	 * @param 		type
	 * 				A BorderType for the new border
	 * 
	 * @param 		square
	 * 				The square to link this border to
	 *
	 * @post		The direction is set on a border.
	 * 				| new.getFirstSquare() == square
	 * 
	 * @post 		The bordertype is set to the given value
	 * 				| new.getBorderType() == type
	 */
	public Border(Square square, BorderType type) {
		setFirstSquare(square);
		borderType = type;	
	}
	
	/**
	 * Constructs a new border
	 * 
	 * @param		square
	 * 				The square to link this border to
	 * 
	 * @post		The border is set to its default value, false.
	 * 				| new.isSlippery() == false
	 * @post 		The border/wall will have a door or not.
	 * 				| new.getBorderType() == BorderType.WALL
	 * @post		The direction is set on a border.
	 * 				| new.getFirstSquare() == square
	 */
	public Border(Square square){
		setFirstSquare(square);
		borderType = BorderType.WALL;
		setSlippery(false);	
	}
	
	/**
	 * Returns the type of the border
	 */
	@Basic
	public BorderType getBorderType(){
		return this.borderType;
	}
	
	/**
	 * A method to check whether a border is a door or not
	 * 
	 * @return	True if the bordertype is "OPEN_DOOR" or "CLOSED_DOOR", false if it isn't
	 * 			| result == ( (getBorderType() == BorderType.OPEN_DOOR) || (getBorderType() == BorderType.CLOSED_DOOR) )
	 */
	public boolean isDoor()
	{
		return getBorderType() == BorderType.OPEN_DOOR || getBorderType() == BorderType.CLOSED_DOOR;
	}
	
	/**
	 * Sets the border type to OPEN_DOOR
	 * 
	 * @effect	Border type is set to OPEN_DOOR
	 * 			| setBorderType(BorderType.OPEN_DOOR)
	 */
	public void openDoor()
	{
		setBorderType(BorderType.OPEN_DOOR);
	}
	
	/**
	 * Sets the border type to CLOSED_DOOR
	 * 
	 * @effect	Border type is set to CLOSED_DOOR
	 * 			| setBorderType(BorderType.CLOSED_DOOR)
	 */
	public void closeDoor()
	{
		setBorderType(BorderType.CLOSED_DOOR);
	}
	
	/**
	 * Sets the type of border
	 * 
	 * @param 		type
	 * 				The BorderType to set
	 * @throws		IllegalArgumentException
	 * 				One of the squares that have this border as a border can't have the given border type as border type
	 * 				| !((getFirstSquare() == null || getFirstSquare().canHaveAsBorderType(type, getFirstSquare().getDirectionFromBorder(this)))
	 *				|	&& 	(getSecondSquare() == null || getSecondSquare().canHaveAsBorderType(type, getSecondSquare().getDirectionFromBorder(this))))
	 * @post 		The new bordertype is set to the given type
	 * 				| new.getBorderType() == type
	 * @post 		If the new bordertype is an open door or "empty" the temperature and humidity for the space will be recalculated
	 * 				| if(type == BorderType.OPEN_DOOR || type == BorderType.EMPTY)
	 *				| 	 if(getFirstSquare() != null)
	 *				|	    	getFirstSquare().mergeSpace();	
	 *				|	 else if(getSecondSquare() != null)
	 *				|		 getSecondSquare().mergeSpace();
	 */
	public void setBorderType(BorderType type) throws IllegalArgumentException{
		if(getBorderType() != type)
		{		
			if( (getFirstSquare() == null || getFirstSquare().canHaveAsBorderType(type, getFirstSquare().getDirectionFromBorder(this)))
				&& 	(getSecondSquare() == null || getSecondSquare().canHaveAsBorderType(type, getSecondSquare().getDirectionFromBorder(this))))
			{
				this.borderType = type;				
				if(type == BorderType.OPEN_DOOR || type == BorderType.EMPTY)
				{
					// We can use casts here because an exception will be thrown when the type is an open door or empty one of the squares is a rock
					if(getFirstSquare() != null)
						((RegularSquare) getFirstSquare()).mergeSpace();	
					else if(getSecondSquare() != null)
						((RegularSquare) getSecondSquare()).mergeSpace();
				}
			}
			else
				throw new IllegalArgumentException();
		}
	}
	
	/**
	 * A variable that resembles the type of border
	 */
	private BorderType borderType;
	
	/**
	 * Returns the first square attached to the border
	 */
	@Basic
	public Square getFirstSquare(){
		return borderSquares[0];
	}
	
	/**
	 * Returns the second square attached to the border
	 */
	@Basic
	public Square getSecondSquare(){
		return borderSquares[1];
	}

	/**
	 * Sets the given square to the used variable in this method.
	 * 
	 * @param 		square
	 * 				Given square on which the border is part of.
	 * 
	 * @post 		The given square value is linked to this border
	 * 				| new.getFirstSquare() == square
	 */
	@Raw
	public void setFirstSquare(Square square){
		borderSquares[0] = square;
	}
	
	/**
	 * Sets the given square to the used variable in this method.
	 * 
	 * @param 		square
	 * 				Given square on which the border is part of.
	 * 
	 * @post 		The given square value is linked to this border
	 * 				| new.getSecondSquare() == square
	 */
	public void setSecondSquare(Square secondSquare){
		borderSquares[1] = secondSquare;
	}
	
	/**
	 * Array that keeps track of the 2 squares that share this border.
	 */
	private Square[] borderSquares = new Square[2];

	
	/**
	 * Returns the other square linked to the border
	 * @param 	currentSquare
	 * 			The square you already know
	 * @return	The other square linked to the border
	 * 			| if (getFirstSquare() == currentSquare)
	 *			|	result == getSecondSquare();
	 *			| else
	 *			| 	result == getFirstSquare();
	 */
	public Square getOtherSquare(Square currentSquare){
		if (getFirstSquare() == currentSquare)
			return getSecondSquare();
		else
			return getFirstSquare();
	}
	
		
	/**
	 * Returns true if the wall is slippery, false if it isn't
	 * 
	 * @throws	IllegalStateException
	 * 			The square's bordertype isn't a WALL
	 * 			|  this.getBorderType() != BorderType.WALL 
	 */
	@Basic
	public boolean isSlippery() throws IllegalStateException
	{
		if( this.getBorderType() != BorderType.WALL )
			throw new IllegalStateException();
		else
			return this.slipperyWall;
	}
	
	/**
	 * Sets the whether the wall is slippery or not
	 * 
	 * @param 		slippery
	 * 				Given slippery status
	 * 
	 * @post 		The wall is made slippery
	 * 				| new.isSlippery() == slippery
	 * 
	 * @throws		IllegalStateException
	 * 				The square's bordertype isn't a WALL
	 * 				| this.getBorderType() != BorderType.WALL 
	 */
	@Raw
	private void setSlippery(boolean slippery) throws IllegalStateException 
	{
		if( this.getBorderType() != BorderType.WALL )
			throw new IllegalStateException();
		this.slipperyWall = slippery;
	}
	
	/**
	 * Boolean representing whether the wall is slippery or not
	 */
	private boolean slipperyWall = false;
	
	
	/**
	 * 
	 * Returns the most closed border out of 2 
	 * 
	 * @param 	other
	 * 			The other border to compare with
	 * @return	The border thats the most closed
	 * 			| if( this.getBorderType() == BorderType.WALL )
	 *			|	result == this
	 *			| else if( other.getBorderType() == BorderType.WALL )
	 *			|	result == other
	 *			| else if( this.getBorderType() == BorderType.CLOSED_DOOR )
	 *			|	result == this
	 *			| else if( other.getBorderType() == BorderType.CLOSED_DOOR )
	 *			|	result == other
	 *			| else if( this.getBorderType() == BorderType.OPEN_DOOR )
	 *			|	result == this
	 *			| else if( other.getBorderType() == BorderType.OPEN_DOOR )
	 *			|	result == other
	 *			| else
	 *			|	result == this
	 */
	public Border getMostClosedBorder( Border other )
	{
		if( this.getBorderType() == BorderType.WALL )
			return this;
		else if( other.getBorderType() == BorderType.WALL )
			return other;
		else if( this.getBorderType() == BorderType.CLOSED_DOOR )
			return this;
		else if( other.getBorderType() == BorderType.CLOSED_DOOR )
			return other;
		else if( this.getBorderType() == BorderType.OPEN_DOOR )
			return this;
		else if( other.getBorderType() == BorderType.OPEN_DOOR )
			return other;
		else
			return this;	
	}
	

}









